/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = ora_ttc7_oopen.c                                   */
/*  NOTE      =                                                    */
/*  DATE      = 2014/11/24 by songbb                               */
/*******************************************************************/

#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>

#include "frcomm.h"
#include "frstr.h"
#include "frmem.h"
#include "frhash2.h"
#include "frlog.h"

#include "conntrack.h"
#include "frct_comm.h"

#include "../../ora_core.h"
#include "../../comm/ora_comm.h"
#include "../../comm/ora_stmt.h"
#include "../../comm/ora_util.h"
#include "../ora_mar_engine.h"
#include "ora_ttc7_tti_oer.h"

/*******************************************************************************
*  FUNC     :  解析ttc7的open请求
*  ARGS     :  *p_st_conn                           (ct信息)
*           :  *p_st_sess                           (Oracle私有的session)
*           :  *p_st_ora_packet                     (Oracle数据包)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int ora_ttc7_oopen_unmarshal_req(Conntrack *p_st_conn
                                , L7session_ora *p_st_sess
                                , L7_ora_packet *p_st_ora_packet)
{
    Sql_statement *p_st_stmt = NULL;
    char c_tmp = 0;
    short s_tmp = 0;
    short s_byte_1 = 0;
    short s_byte_2 = 0;
    long long ll_tmp = 0LL;
    int i_use_oci_old = p_st_sess->i_use_oci;
    int i_use_special_field_old = p_st_sess->i_use_special_field;
    int i_ptr_len_old = p_st_sess->i_ptr_len;
    int i_need_detect = FR_NO;
    int i_remaining = 0;
    int i_start_position = 0;
    uint64_t uint64_tmp = 0LL;

    if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_tmp) != FR_SUCC) // seqNumber
        goto error;

    if (p_st_sess->i_use_oci == APM_ORA_FLAG_UNSET)
    {
        i_need_detect = FR_YES;
    }

    if (i_need_detect)
    {
        i_start_position = bytebuff_getPosition(p_st_ora_packet->p_st_packet);

        if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_byte_1) != FR_SUCC)
            goto error;
        i_remaining = bytebuff_remaining(p_st_ora_packet->p_st_packet);
        if (i_remaining < 1)
            goto error;

        /**
         * 0x030200 01 00 jdbc
         * 0x030200 01 00  030200 01 00 jdbc
         * 0x030200 01 00  030200 01 00  0347... jdbc
         * 0x030200 01 00  0347... jdbc
         * 0x030200 01 00000000 oci
         * 0x030200 01a04e02 00000000 oci
         * 0x030200 30a06702 00000000 oci
         * 0x030200 fe871a9e 00000000 oci
         * 0x030200 feffffff 00000000 oci
         * 0x030200 feffffffffffffff 00000000 oci
         */
        if (s_byte_1 == 0x01)
        {
            if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_byte_2) != FR_SUCC)
                goto error;
            if (i_remaining == 1)
            {
                if (s_byte_2 == 0x00)
                {
                    /**
                     * 0x030200 01 00 jdbc
                     */
                    p_st_sess->i_use_oci = FR_NO;
                    p_st_sess->i_use_special_field = FR_NO;
                }
                else
                    goto error;
            }
            else if (i_remaining == 4)
            {
                if (bytebuff_peek_long(p_st_ora_packet->p_st_packet, 3, &uint64_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
                    goto error;
                if (uint64_tmp == 0x000000)
                {
                    /**
                     * 0x030200 01 00000000 oci
                     */
                    p_st_sess->i_use_oci = FR_YES;
                    p_st_sess->i_use_special_field = FR_YES; // TODO: aix这个可能为FR_NO
                    p_st_sess->i_ptr_len = APM_ORA_PTR_LEN_1;
                }
                else
                    goto error;
            }
            else if (i_remaining == 7)
            {
                if (ora_mar_skip(p_st_ora_packet, 3) != FR_SUCC)
                    goto error;
                if (bytebuff_peek_long(p_st_ora_packet->p_st_packet, 4, &uint64_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
                    goto error;
                if (uint64_tmp == 0x00000000)
                {
                    /**
                     * 0x030200 01a04e02 00000000 oci
                     */
                    p_st_sess->i_use_oci = FR_YES;
                    p_st_sess->i_use_special_field = FR_YES;
                    p_st_sess->i_ptr_len = APM_ORA_PTR_LEN_4;
                }
                else
                    goto error;
            }
            else if (i_remaining >= 6)
            {
                if (bytebuff_peek_long(p_st_ora_packet->p_st_packet, 2, &uint64_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
                    goto error;
                if (uint64_tmp == 0x0302 || uint64_tmp == 0x0347)
                {
                    /**
                     * 0x030200 01 00  030200 01 00 jdbc
                     * 0x030200 01 00  030200 01 00  0347... jdbc
                     * 0x030200 01 00  0347... jdbc
                     */
                    p_st_sess->i_use_oci = FR_NO;
                    p_st_sess->i_use_special_field = FR_NO;
                }
                else
                    goto error;
            }
            else
                goto error;
        }
        else if (i_remaining == 7)
        {
            if (ora_mar_skip(p_st_ora_packet, 3) != FR_SUCC)
                goto error;
            if (bytebuff_peek_long(p_st_ora_packet->p_st_packet, 4, &uint64_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
                goto error;
            if (uint64_tmp == 0x00000000)
            {
                /**
                 * 0x030200 30a06702 00000000 oci
                 * 0x030200 fe871a9e 00000000 oci
                 * 0x030200 feffffff 00000000 oci
                 */
                p_st_sess->i_use_oci = FR_YES;
                p_st_sess->i_use_special_field = FR_YES;
                p_st_sess->i_ptr_len = APM_ORA_PTR_LEN_4;
            }
            else
                goto error;
        }
        else if (i_remaining == 11)
        {
            if (ora_mar_skip(p_st_ora_packet, 7) != FR_SUCC)
                goto error;
            if (bytebuff_peek_long(p_st_ora_packet->p_st_packet, 4, &uint64_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
                goto error;
            if (uint64_tmp == 0x00000000)
            {
                /**
                 * 0x030200 feffffffffffffff 00000000 oci
                 */
                p_st_sess->i_use_oci = FR_YES;
                p_st_sess->i_use_special_field = FR_YES;
                p_st_sess->i_ptr_len = APM_ORA_PTR_LEN_8;
            }
            else
                goto error;
        }
        else
            goto error;

        if (bytebuff_setPosition(p_st_ora_packet->p_st_packet, i_start_position) != FR_SUCC)
            goto error;

        if (p_st_sess->i_use_oci == FR_YES && p_st_sess->i_use_special_field == FR_YES)
        {
            ora_set_ttc_type_rep(p_st_sess, 0, 0);
            ora_set_ttc_type_rep(p_st_sess, 1, 2);
            ora_set_ttc_type_rep(p_st_sess, 2, 2);
            ora_set_ttc_type_rep(p_st_sess, 3, 2);
            ora_set_ttc_type_rep(p_st_sess, 4, 0);
        }
        else
            ora_set_ttc_type_rep_jdbc(p_st_sess);

        ORA_PRINT_DBG("set i_use_oci = %s, i_use_special_field = %s, i_ptr_len = %d\n",
                      p_st_sess->i_use_oci == APM_ORA_FLAG_UNSET ? "UNSET" : (p_st_sess->i_use_oci == FR_YES ? "FR_YES" : "FR_NO"),
                      p_st_sess->i_use_special_field == APM_ORA_FLAG_UNSET ? "UNSET" : (p_st_sess->i_use_special_field == FR_YES ? "FR_YES" : "FR_NO"),
                      p_st_sess->i_ptr_len);
    }

    if (ora_mar_unmarshalO2U(p_st_sess, p_st_ora_packet, &c_tmp) != FR_SUCC) // cidO2U
        goto error;

    if (ora_mar_unmarshalUWORD(p_st_sess, p_st_ora_packet, &ll_tmp) != FR_SUCC) // opesiz
        goto error;

    if (p_st_sess->i_use_oci == FR_YES && bytebuff_remaining(p_st_ora_packet->p_st_packet) == 4)
    {
        if (ora_mar_skip(p_st_ora_packet, 4) != FR_SUCC)
            goto error;
    }

    // 新建一个游标为0的 Sql_statement
    if (ora_push_ttc7_new_stmt(p_st_conn, &p_st_stmt) != FR_SUCC)
        goto error;

    // 设置l4层信息
    if (frdb_set_resultset_req(p_st_stmt, FRDB_LAST, 0, bytebuff_length(p_st_ora_packet->p_st_packet), &(p_st_ora_packet->st_timestamp_first), &(p_st_ora_packet->st_timestamp_last)) != FR_SUCC)
        goto error;

    if (i_need_detect)
    {
        p_st_sess->i_c2snlsratio = 2;

        if (p_st_sess->i_use_oci == FR_NO)
        {
            // XXX: 大部分情况下是这样的
            p_st_sess->s_cli_charset_out = 871; // UTF8
            p_st_sess->s_srv_charset = 852; // ZHS16GBK
            p_st_sess->c_pro_srv_version = 6; // TODO: 待定
            p_st_sess->i_charset_assumed = FR_YES;
        }
        else
        {
            // XXX: 大部分情况下是这样的
            p_st_sess->s_cli_charset_out = 852; // ZHS16GBK
            p_st_sess->s_srv_charset = 852; // ZHS16GBK
            p_st_sess->c_pro_srv_version = 6; // TODO: 待定
            p_st_sess->i_charset_assumed = FR_YES;
        }
    }

    return FR_SUCC;
error:
    p_st_sess->i_use_oci = i_use_oci_old;
    p_st_sess->i_use_special_field = i_use_special_field_old;
    p_st_sess->i_ptr_len = i_ptr_len_old;
    frdb_statement_free(&p_st_stmt);
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  解析ttc7的open响应
*  ARGS     :  *p_st_conn                           (ct信息)
*           :  *p_st_sess                           (Oracle私有的session)
*           :  *p_st_ora_packet                     (Oracle数据包)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int ora_ttc7_oopen_unmarshal_resp(Conntrack *p_st_conn
                                , L7session_ora *p_st_sess
                                , L7_ora_packet *p_st_ora_packet)
{
    L7_session *p_st_session = frct_get_session(p_st_conn);
    L7session_ora_private *p_st_session_private = (L7session_ora_private *) frct_get_session_private(p_st_session);
    Sql_statement *p_st_stmt = NULL;
    int i_i = 0;
    short s_j = 0;
    int i_cid = 0;

    while (i_i == 0)
    {
        if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_j) != FR_SUCC) // j
            goto error;

        switch (s_j)
        {
            case 8:
                if (ora_mar_unmarshalSWORD(p_st_sess, p_st_ora_packet, &i_cid) != FR_SUCC) // 服务端返回的游标值
                    goto error;

                if ((p_st_stmt = ora_pop_ttc7_new_stmt(p_st_conn)) == NULL)
                {
                    ORA_PRINT_WARN("has no ttc7 new stmt\n");
                    goto error;
                }
                // 将游标0更新为从服务端申请回来的游标值
                if (frdb_add_statement(p_st_session_private->p_hash_stmt, i_cid, p_st_stmt) != FR_SUCC)
                    goto error;

                ORA_PRINT_TIMEVAL("set resp time st_timestamp_first", p_st_ora_packet->st_timestamp_first);
                ORA_PRINT_TIMEVAL("set resp time st_timestamp_last", p_st_ora_packet->st_timestamp_last);
                // 设置l4层信息
                if (frdb_set_resultset_resp(p_st_stmt, FRDB_LAST, 0, bytebuff_length(p_st_ora_packet->p_st_packet), 0, NULL, &(p_st_ora_packet->st_timestamp_first), &(p_st_ora_packet->st_timestamp_last)) != FR_SUCC)
                    goto error;

                ORA_PRINT_DBG("add stmt of cursor 0 to hash_stmt and update to cursor:[%d]\n", i_cid);
                break;

            case 4:
                if (ora_ttc7_tti_oer_unmarshal(p_st_conn, p_st_sess, p_st_ora_packet, APM_ORA_SESS_IS_OOPEN, NULL) == FR_FAIL)
                    goto error;
                i_i = 1;
                break;

            case 9:
                i_i = 1;
                break;

            default:
                goto error;
        }
    }

    return FR_SUCC;
error:
    return FR_FAIL;
}
